#include <assert.h>
#include <elf.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define IMAGE_FILE "./image"
#define ARGS "[--extended] [--vm] <bootblock> <executable-file> ..."

#define SECTOR_SIZE 512
#define OS_SIZE_LOC 0x1fc // 实测如果是2的话会覆盖BootLoader的内容导致程序引导失败
#define BOOT_LOADER_SIG_OFFSET 0x1fe
#define BOOT_LOADER_SIG_1 0x55
#define BOOT_LOADER_SIG_2 0xaa
#define BOOT_MEM_LOC 0x7c00
#define OS_MEM_LOC 0x1000

/* structure to store command line options */
static struct
{
    int vm;
    int extended;
} options;

static Elf64_Phdr *read_exec_file(FILE *fp);
static void write_bootblock(FILE *img, FILE *fp, Elf64_Phdr *phdr);
static int kernel_total_size(FILE *fp, Elf64_Phdr *phdr);
static void write_kernel(FILE *img, FILE *fp, Elf64_Phdr *phdr, int kernel_size);
static void write_os_size(FILE *img, int kernel_sector);
static int Count_kernel_sector(int kernel_size);
static void error(char *fmt);
static void print_info(Elf64_Phdr *phdr);

/* prototypes of local functions */
/*
static void create_image(int nfiles, char *files[]);
static void error(char *fmt, ...);
static void read_ehdr(Elf64_Ehdr *ehdr, FILE *fp);
static void read_phdr(Elf64_Phdr *phdr, FILE *fp, int ph,
                      Elf64_Ehdr ehdr);
static void write_segment(Elf64_Ehdr ehdr, Elf64_Phdr phdr, FILE *fp,
                          FILE *img, int *nbytes, int *first);
static void write_os_size(int nbytes, FILE *img);
static void write_user_thread_segment(Elf64_Ehdr ehdr, Elf64_Phdr phdr, FILE *fp,
                                      FILE *img, int *nbytes, int *first);

                                      */
int main(int argc, char **argv)
{
    FILE *bootblock_file, *kernel_file, *img;
    Elf64_Phdr *bootblock_phdr, *kernel_phdr;

    //读取bootblock文件与文件头
    char *bootblock = argv[2];
    bootblock_file = fopen(bootblock, "rb");
    //bootblock_file = fopen("bootblock", "rb");
    bootblock_phdr = read_exec_file(bootblock_file);

    //打开img镜像
    img = fopen("image", "wb");
    write_bootblock(img, bootblock_file, bootblock_phdr);

    //读取kernel文件、获得kernel大小并写入镜像
    int kernel_size;
    char *kernel = argv[3];
    kernel_file = fopen(kernel, "rb");
    //kernel_file = fopen("main", "rb");
    kernel_phdr = read_exec_file(kernel_file);
    kernel_size = kernel_total_size(kernel_file, kernel_phdr);
    write_kernel(img, kernel_file, kernel_phdr, kernel_size);

    //获取kernel的扇区个数
    int kernel_sector;
    kernel_sector = Count_kernel_sector(kernel_size);
    write_os_size(img, kernel_sector);

    //关闭打开的文件
    fclose(bootblock_file);
    fclose(kernel_file);
    fclose(img);

    //释放指针
    free(bootblock_phdr);
    free(kernel_phdr);

    char *progname = argv[0];

    /* process command line options */
    options.vm = 0;
    options.extended = 0;
    while ((argc > 1) && (argv[1][0] == '-') && (argv[1][1] == '-'))
    {
        char *option = &argv[1][2];

        if (strcmp(option, "vm") == 0)
        {
            options.vm = 1;
        }
        else if (strcmp(option, "extended") == 0)
        {
            options.extended = 1;
            printf("0xa0800000: bootblock\n");
            print_info(bootblock_phdr);
            printf("0xa0800000: main\n");
            print_info(kernel_phdr);
        }
        argc--;
        argv++;
    }

    return 0;
}

//读取可执行文件的程序头
static Elf64_Phdr *read_exec_file(FILE *fp)
{
    Elf64_Phdr *phdr = (Elf64_Phdr *)malloc(sizeof(Elf64_Phdr));
    Elf64_Ehdr ehdr;

    fread(&ehdr, sizeof(Elf64_Ehdr), 1, fp);
    fseek(fp, ehdr.e_phoff, SEEK_SET);
    fread(phdr, sizeof(Elf64_Phdr) * (ehdr.e_phnum), 1, fp);

    return phdr;
}

//将bootblock程序段写入镜像
static void write_bootblock(FILE *img, FILE *fp, Elf64_Phdr *phdr)
{
    char *buffer = (char *)malloc(SECTOR_SIZE * sizeof(char));
    char zero[SECTOR_SIZE] = "";
    fseek(fp, phdr->p_offset, SEEK_SET);
    fread(buffer, phdr->p_filesz, 1, fp);
    fwrite(buffer, phdr->p_filesz, 1, img);

    uint8_t sig_1 = BOOT_LOADER_SIG_1;
    uint8_t sig_2 = BOOT_LOADER_SIG_2;

    //未充满的扇区补零
    if (phdr->p_filesz % SECTOR_SIZE != 0)
    {
        int last = SECTOR_SIZE - phdr->p_filesz % SECTOR_SIZE;
        fwrite(zero, 1, last - 2, img);
        fwrite(&sig_1, 1, 1, img);
        fwrite(&sig_2, 1, 1, img);
    }

    free(buffer);
}

//获取kernel的总大小
static int kernel_total_size(FILE *fp, Elf64_Phdr *phdr)
{
    Elf64_Ehdr ehdr;
    int total_size = 0;

    //读取kernel的文件头
    fseek(fp, 0, SEEK_SET);
    fread(&ehdr, sizeof(Elf64_Ehdr), 1, fp);
    //fseek(fp, 0, SEEK_SET);

    //将每一个程序头读入phdr_array中
    Elf64_Phdr phdr_array[ehdr.e_phnum];
    fseek(fp, ehdr.e_phoff, SEEK_SET);
    fread(phdr_array, sizeof(Elf64_Phdr), ehdr.e_phnum, fp);

    for (int i = 0; i < ehdr.e_phnum; i++)
        total_size += phdr_array[i].p_memsz;

    fseek(fp, 0, SEEK_SET);
    printf("kernel_size = %d\n", total_size);
    return total_size;
}

//将kernel程序段写入镜像
static void write_kernel(FILE *img, FILE *fp, Elf64_Phdr *phdr, int kernel_size)
{
    //获取kernel的文件头
    Elf64_Ehdr ehdr;
    fseek(fp, 0, SEEK_SET);
    fread(&ehdr, sizeof(Elf64_Ehdr), 1, fp);

    char zero[SECTOR_SIZE] = "";
    char *buffer = (char *)malloc(kernel_size * sizeof(char));

    //将每一个程序头读入phdr_array中
    Elf64_Phdr phdr_array[ehdr.e_phnum];
    printf("e_phnum = %d\n", ehdr.e_phnum);
    fseek(fp, ehdr.e_phoff, SEEK_SET);
    fread(phdr_array, sizeof(Elf64_Phdr), ehdr.e_phnum, fp);

    //将kernel的每个程序段依次读入buffer并写入img镜像
    for (int i = 0; i < ehdr.e_phnum; i++)
    {
        fseek(fp, phdr_array[i].p_offset, SEEK_SET);
        fread(buffer, phdr_array[i].p_filesz, 1, fp);
        fwrite(buffer, phdr_array[i].p_filesz, 1, img);
        fwrite(zero, 1, phdr_array[i].p_memsz - phdr_array[i].p_filesz, img);
    }

    if (kernel_size % SECTOR_SIZE != 0)
    {
        int last = SECTOR_SIZE - kernel_size % SECTOR_SIZE;
        fwrite(zero, 1, last, img);
    }
    free(buffer);
}

//将kernel的扇区数写入bootblock
static void write_os_size(FILE *img, int kernel_sector)
{
    fseek(img, OS_SIZE_LOC, SEEK_SET);
    //fseek(img, 510, SEEK_SET);
    printf("kernel_sector=%d\n\n", kernel_sector);
    fwrite(&kernel_sector, sizeof(int), 1, img);
}

//计算kernel的扇区数
static int Count_kernel_sector(int kernel_size)
{
    int count;
    if (kernel_size % SECTOR_SIZE == 0)
        count = kernel_size / SECTOR_SIZE;
    else
        count = kernel_size / SECTOR_SIZE + 1;

    return count;
}

//参数extend时完成相应信息的打印
static void print_info(Elf64_Phdr *phdr)
{
    printf("    offset %lx", phdr->p_offset);
    printf("    vaddr  %lx\n", phdr->p_vaddr);
    printf("    filesz %lx", phdr->p_filesz);
    printf("    memsz  %lx\n", phdr->p_memsz);

    printf("    writing %lx bytes\n", phdr->p_memsz);
}